package homework.score.util;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;

public class ReflectionUtil {

    private ReflectionUtil() {}

    private static String upperFirstChar(String s) {
        StringBuilder builder = new StringBuilder(s);

        String c = builder.substring(0, 1).toUpperCase();

        builder.replace(0, 1, c);

        return builder.toString();
    }

    private static String lowerFirstChar(String s) {
        StringBuilder builder = new StringBuilder(s);

        String c = builder.substring(0, 1).toLowerCase();

        builder.replace(0, 1, c);

        return builder.toString();
    }

    public static List<Method> getStartsWithMethod(Object object, String prefix) {
        return getStartsWithMethod(object.getClass(), prefix);
    }

    public static List<Method> getStartsWithMethod(Class clazz, String prefix) {

        Method[] methods = clazz.getDeclaredMethods();
        List<Method> result = new ArrayList<Method>();

        for (Method method : methods) {
            if (method.getName().startsWith(prefix)) {
                result.add(method);
            }
        }

        return result;
    }

    private static Method getMethodPrefixLength(Object o, String fieldName, String prefix, int paramCount) {

        String methodName = prefix + upperFirstChar(fieldName);

        Method method = null;

        try {
            method = o.getClass().getDeclaredMethod(methodName);

            // method.getParameterTypes().length
            if (method.getParameterCount() != paramCount) {
                method = null;
            }
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }

        return method;
    }

    public static Method getGetter(Object o, String fieldName) {
        return getMethodPrefixLength(o, fieldName, "get", 0);
    }

    public static Method getSetter(Object o, String fieldName) {
        Method[] methods = o.getClass().getMethods();

        for (Method m : methods) {
            if (m.getName().equals("set" + upperFirstChar(fieldName))) {
                return m;
            }
        }

        return null;
    }

    public static List<Method> getGetters(Object object) {
        List<Method> methods = getStartsWithMethod(object, "get");
//        Map<String, Class> fields = getFields(object);

        for (Iterator it = methods.iterator(); it.hasNext();) {
            Method m = (Method) it.next();

            String name = upperFirstChar(m.getName().substring(3));

            Field field = null;

            try {
                field = object.getClass().getDeclaredField(name);
            } catch (NoSuchFieldException ignored) {

            }

            // method.getParameterTypes().length
            if (m.getParameterCount() != 0 || field == null) {
                it.remove();
            }
        }

        return methods;
    }

//    public static List<Method> getSetters(Object object) {
//        List<Method> methods = getStartsWithMethod(object, "set");
//
//        for (Iterator it = methods.iterator(); it.hasNext();) {
//            Method m = (Method) it.next();
//
//            String name = upperFirstChar(m.getName().substring(3));
//
//            Field field = null;
//
//            try {
//                field = object.getClass().getDeclaredField(name);
//            } catch (NoSuchFieldException ignored) {
//
//            }
//
//            if (m.getParameterCount() != 1 || field == null) {
//                it.remove();
//            }
//        }
//
//        return methods;
//    }

    public static Map<String, String> getProperties(Object object) {

        Map<String, String> result = new HashMap<String, String>();

        Field[] fields = object.getClass().getDeclaredFields();

        for (Field field : fields) {

            Method getter = getGetter(object, field.getName());

            if (getter != null) {

                String name = field.getName();

                String value;
                try {
                    value = getter.invoke(object).toString();
                    result.put(name, value);
                } catch (IllegalAccessException | InvocationTargetException e) {
                    e.printStackTrace();
                }
            }

        }

        return result;
    }
}
